package com.jcode.skeleton.dao;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.ibatis.mapping.BoundSql;
import org.mybatis.spring.SqlSessionTemplate;

import com.jcode.skeleton.model.Page;
import com.jcode.skeleton.model.Pageable;

/**
 * 
 * @Desc Mybatis通用Dao，只支持MySQL分页（无需插件），多库读写分离
 * 
 * @Author dreajay
 * @DateTime 2016年8月3日 下午2:24:17
 *
 */
public class GenericDaoMybatisImpl implements GenericDao {
	private SqlSessionTemplate singleSqlSession;

	private SqlSessionTemplate[] readSqlSessions;
	private SqlSessionTemplate[] writeSqlSessions;

	private ConcurrentHashMap<Integer, SqlSessionTemplate> readSqlSessionMap = new ConcurrentHashMap<Integer, SqlSessionTemplate>();
	private ConcurrentHashMap<Integer, SqlSessionTemplate> writeSqlSessionMap = new ConcurrentHashMap<Integer, SqlSessionTemplate>();

	private volatile AtomicInteger readCount = new AtomicInteger(0);
	private volatile AtomicInteger writeCount = new AtomicInteger(0);

	/**
	 * 使用单一数据源构造MybatisDao，读写使用相同的SqlSessionTemplate
	 * 
	 * @param sqlSession
	 */
	public GenericDaoMybatisImpl(SqlSessionTemplate sqlSession) {
		this.singleSqlSession = sqlSession;
	}

	/**
	 * 使用读写分离数据源构造MybatisDao
	 * 
	 * @param readSqlSessions
	 * @param writeSqlSessions
	 */
	public GenericDaoMybatisImpl(SqlSessionTemplate[] readSqlSessions, SqlSessionTemplate[] writeSqlSessions) {
		setReadSqlSessions(readSqlSessions);
		setWriteSqlSessions(writeSqlSessions);
	}

	public SqlSessionTemplate[] getReadSqlSessions() {
		return readSqlSessions;
	}

	public void setReadSqlSessions(SqlSessionTemplate[] readSqlSessions) {
		this.readSqlSessions = readSqlSessions;
		for (int i = 0; i < readSqlSessions.length; i++) {
			readSqlSessionMap.put(i, readSqlSessions[i]);
		}
	}

	public SqlSessionTemplate[] getWriteSqlSessions() {
		return writeSqlSessions;
	}

	public void setWriteSqlSessions(SqlSessionTemplate[] writeSqlSessions) {
		this.writeSqlSessions = writeSqlSessions;
		for (int i = 0; i < readSqlSessions.length; i++) {
			writeSqlSessionMap.put(i, writeSqlSessions[i]);
		}
	}

	/**
	 * 获取SqlSessionTemplate，优先获取读库SqlSessionTemplate，不存在时获取写库SqlSessionTemplate
	 * 
	 * @return
	 */
	public SqlSessionTemplate getSqlSessionTemplate() {
		SqlSessionTemplate sqlSessionTemplate = getReadSqlSession();
		return sqlSessionTemplate == null ? getWriteSqlSession() : sqlSessionTemplate;
	}

	/**
	 * 获取读库SqlSessionTemplate
	 * 
	 * @return
	 */
	public SqlSessionTemplate getReadSqlSession() {
		if (singleSqlSession != null) {
			return singleSqlSession;
		}
		int index = Math.abs(readCount.getAndIncrement() % readSqlSessionMap.size());
		SqlSessionTemplate sqlSessionTemplate = readSqlSessionMap.get(index);
		if (sqlSessionTemplate == null) {
			throw new RuntimeException("cannot get the db connection, please check the db is running.");
		}
		return sqlSessionTemplate;
	}

	/**
	 * 获取写库SqlSessionTemplate
	 * 
	 * @return
	 */
	public SqlSessionTemplate getWriteSqlSession() {
		if (singleSqlSession != null) {
			return singleSqlSession;
		}
		int index = Math.abs(writeCount.getAndIncrement() % writeSqlSessionMap.size());
		SqlSessionTemplate sqlSessionTemplate = writeSqlSessionMap.get(index);
		if (sqlSessionTemplate == null) {
			throw new RuntimeException("cannot get the db connection, please check the db is running.");
		}
		return sqlSessionTemplate;
	}

	/**
	 * 保存对象
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public int insert(String statement, Object parameter) {
		return getWriteSqlSession().insert(statement, parameter);
	}

	/**
	 * 更新对象
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public int update(String statement, Object parameter) {
		return getWriteSqlSession().update(statement, parameter);
	}

	/**
	 * 删除对象
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public int delete(String statement, Object parameter) {
		return getWriteSqlSession().delete(statement, parameter);
	}

	/**
	 * 批量删除对象
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public int batchDelete(String statement, Object parameter) {
		return getWriteSqlSession().delete(statement, parameter);
	}

	/**
	 * 查询对象数目
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public int count(String statement, Object parameter) {
		return (Integer) getReadSqlSession().selectOne(statement, parameter);
	}

	/**
	 * 查询单个对象
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public <T> T selectOne(String statement, Object parameter) {
		return getReadSqlSession().selectOne(statement, parameter);
	}

	/**
	 * 查询所有对象
	 * 
	 * @param parameter
	 * @return
	 */
	public <T> List<T> selectAll(String statement, Object parameter) {
		return getReadSqlSession().selectList(statement, parameter);
	}

	/**
	 * 自定义查询对象列表
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public <T> List<T> selectList(String statement, Object parameter) {
		return getReadSqlSession().selectList(statement, parameter);
	}

	/**
	 * 分页查询，暂只支持MySQL limit用法
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	public Page selectListByPage(String statement, Pageable parameter) {
		int totalRows = getCount(statement, parameter);
		List<?> list = getReadSqlSession().selectList(statement, parameter);
		Page page = parameter.getPage();
		page.setTotalRows(totalRows);
		page.setList(list);
		return page;
	}

	/**
	 * 根据查询语句的statement计算count，忽略limit参数，只限MySQL
	 * 
	 * @param statement
	 * @param parameter
	 * @return
	 */
	protected int getCount(String statement, Pageable parameter) {
		SqlSessionTemplate sqlSessionTemplate = getReadSqlSession();
		BoundSql boundSql = sqlSessionTemplate.getConfiguration().getMappedStatement(statement).getBoundSql(parameter);
		String sql = boundSql.getSql();
		int index = sql.indexOf("limit");
		if (index > 0) {
			sql = sql.substring(0, index);
		}
		sql = String.format("select count(*) from ( %s ) count_table", sql);
		int totalRows = (Integer) sqlSessionTemplate.selectOne("MybatisMapper.sql", sql);
		return totalRows;
	}

}
